package org.kitten.core.util;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.List;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentFactory;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.Namespace;
import org.dom4j.QName;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.jaxen.JaxenException;
import org.jaxen.SimpleNamespaceContext;
import org.jaxen.dom4j.Dom4jXPath;

/**
 * 
 * @author cyper
 * @version 1.0, Apr 17, 2008
 */
public class Dom4jUtil {
	public final static int OUTPUT_FORMAT_COMPACT = 1;// 紧凑格式
	public final static int OUTPUT_FORMAT_PRETTY = 2;// 漂亮格式

	/**
	 * 根据XML文件的全路径名得到Document对象
	 * 
	 * @param xmlFileName
	 * @return
	 * @throws DocumentException
	 */
	public static Document getDocument(String xmlFileName)
			throws DocumentException {
		Document document = null;
		SAXReader saxReader = new SAXReader();
		saxReader.setValidation(false);
		saxReader.setIncludeInternalDTDDeclarations(false);
		saxReader.setIncludeExternalDTDDeclarations(false);
		// saxReader.setFeature(Constants.XERCES_FEATURE_PREFIX
		// + Constants.LOAD_EXTERNAL_DTD_FEATURE, false);

		document = saxReader.read(new File(xmlFileName));
		return document;
	}

	/**
	 * 以指定编码(UTF-8,UTF-8,GB2312,BIG5,..)，指定format(1,2) 输出document到指定的输出流中.
	 * 
	 * @param document
	 * @param os
	 *            指定的输出流
	 * @param encoding
	 *            指定编码
	 * @param format
	 *            指定format(OUTPUT_FORMAT_COMPACT,OUTPUT_FORMAT_PRETTY)
	 * @throws IOException
	 */
	public static void save(Document document, OutputStream os,
			String encoding, int format) throws IOException {
		OutputFormat of = null;
		if (format == OUTPUT_FORMAT_COMPACT) {
			of = OutputFormat.createCompactFormat();
		} else {
			of = OutputFormat.createPrettyPrint();
		}
		of.setEncoding(encoding);

		XMLWriter writer;
		writer = new XMLWriter(os);
		writer.write(document);
		writer.close();
	}

	/**
	 * 以UTF-8的编码方式保存document对象
	 * 
	 * @param document
	 * @param xmlFileName
	 * @throws IOException
	 */
	public static void saveDocument(Document document, String xmlFileName)
			throws IOException {
		OutputFormat format = OutputFormat.createPrettyPrint();
		format.setEncoding("UTF-8");
		XMLWriter writer;
		writer = new XMLWriter(new FileWriter(new File(xmlFileName)), format);
		writer.write(document);
		writer.close();
	}

	/**
	 * 针对带默认命名空间的XML文件,是对selectSingleNode的扩充．
	 * 
	 * @param document
	 * @param unhandledXPath
	 * @param defaultNamespace
	 * @return
	 */
	public static Element selectSingleNode(Document document,
			String unhandledXPath, String defaultNamespace) {
		HashMap<String, String> map = new HashMap<String, String>();
		map.put("prefix", defaultNamespace);
		String newXPath = unhandledXPath.replaceAll("/", "/prefix:");
		// logger.info("【selectSingleNode】"+newXPath);
		Element node = null;
		org.dom4j.XPath x = document.createXPath(newXPath);
		x.setNamespaceURIs(map);
		node = (Element) x.selectSingleNode(document);
		return node;
	}

	public static Element selectSingleNodeWithSpecialXpath(Document document,
			String unhandledXPathPart1, String unhandledXPathPart2,
			String defaultNamespace) {
		HashMap<String, String> map = new HashMap<String, String>();
		map.put("prefix", defaultNamespace);
		String newXPath = unhandledXPathPart1.replaceAll("/", "/prefix:");
		newXPath = newXPath + unhandledXPathPart2;
		// logger.info("newXPath【" + newXPath + "】");
		Element node = null;
		org.dom4j.XPath x = document.createXPath(newXPath);
		x.setNamespaceURIs(map);
		node = (Element) x.selectSingleNode(document);
		return node;
	}

	/**
	 * 针对带默认命名空间的XML文件,是对selectNodes的扩充．
	 * 
	 * @param document
	 * @param unhandledXPath
	 * @param defaultNamespace
	 * @return
	 * @throws JaxenException
	 */
	public static List<Element> selectNodes(Document document,
			String unhandledXPath, String defaultNamespace)
			throws JaxenException {
		HashMap<String, String> map = new HashMap<String, String>();
		map.put("prefix", defaultNamespace);
		String newXPath = unhandledXPath.replaceAll("/", "/prefix:");
		List<Element> nodeList = null;
		org.jaxen.XPath xpath = new Dom4jXPath(newXPath);
		xpath.setNamespaceContext(new SimpleNamespaceContext(map));
		nodeList = xpath.selectNodes(document);
		return nodeList;
	}

	public static Document createDocument() {
		Document document = DocumentHelper.createDocument();
		return document;
	}

	/**
	 * 创建一个空元素，一定要加Namespace，不然会多出一个空的xmlns命名空间.<br>
	 * namespace很好取得，可以用parentNode.getNamespace().<br>
	 * 也可以用refNode.getNamespace().
	 * 
	 * @param name
	 * @param ns
	 * @return
	 */
	public static Element createElement(String name, Namespace ns) {
		QName qname = new QName(name, ns);
		Element ret = DocumentFactory.getInstance().createElement(qname);
		return ret;
	}

	public static Element addElement(Element bean, String name, String text) {
		Element newElement = bean.addElement(name);
		if (text != null) {
			newElement.setText(text);
		}
		return newElement;
	}

	public static Element insertFirst(String name, Element parent) {
		Element newNode = createElement(name, parent.getNamespace());
		List elements = parent.elements();
		elements.add(0, newNode);
		return newNode;
	}

	public static Element insertLast(String name, Element parent) {
		Element newNode = createElement(name, parent.getNamespace());
		parent.add(newNode);
		return newNode;
	}

	public static Element insertBefore(String newNode, Element refNode) {
		Element ret = createElement(newNode, refNode.getNamespace());
		Element parent = refNode.getParent();
		List elements = parent.elements();
		elements.add(elements.indexOf(refNode), ret);
		return ret;
	}

	public static Element insertAfter(String newNode, Element refNode) {
		Element ret = createElement(newNode, refNode.getNamespace());
		Element parent = refNode.getParent();
		List elements = parent.elements();
		elements.add(elements.indexOf(refNode) + 1, ret);
		return ret;
	}

	/**
	 * 将document字符串转化为dom对象
	 * 
	 * @param docString
	 * @param encoding
	 * @return
	 * @throws UnsupportedEncodingException
	 * @throws DocumentException
	 */
	public static Document buildDocumentFromString(String docString,
			String encoding) throws UnsupportedEncodingException,
			DocumentException {
		if (docString == null)
			return null;
		ByteArrayInputStream bin = new ByteArrayInputStream(docString
				.getBytes(encoding));
		SAXReader sax = new SAXReader();
		Document doc = sax.read(bin);
		return doc;
	}

	public static void main(String[] args) throws Exception {
		Document doc = Dom4jUtil.createDocument();
		Element root = doc.addElement("jdbc");
		Element config1 = root.addElement("config")
				.addAttribute("name", "test");
		config1.addElement("jar").addText("d:/jars/myjar.jar");
		config1.addElement("url").addText("jdbc:mysql");
		config1.addElement("username").addText("orasuser");
		config1.addElement("password").addText("orasuser");
		Dom4jUtil.save(doc, new FileOutputStream("c:/test.xml"), "UTF-8",
				Dom4jUtil.OUTPUT_FORMAT_PRETTY);
	}
}
